import random
from datetime import date, timedelta

from django.conf import settings
from django.db import models
from django.db.models import Q
from django.db.models.signals import pre_save
from django.dispatch import receiver
from django.urls import reverse
from django.utils.safestring import mark_safe
from django.utils.translation import gettext_lazy as _


project_status_choices = (
    (1, "待审核"),
    (2, "可用"),
    (3, "审批未通过"),
    (4, "已经关闭"),
)

application_status_choices = (
    (1, "待提交"),
    (2, "待审批"),
    (3, "审批通过，未打款"),
    (4, "已打款，未确认"),
    (5, "审批未通过"),
    (6, "确认收款，完成"),
    (7, "申请失败，完成"),
)

pay_channel_choices = (
    (1, '银行卡'),
    (2, '支付宝'),
    (3, '微信'),
)

direction_choices = (
    (1, '平'),
    (2, '借'),
    (3, '贷'),
)

application_type_categories_choices = (
    (1, '市场费用'),
    (2, '销售费用'),
    (3, '管理费用'),
)


class UserRelation(models.Model):
    leader = models.ForeignKey(
        settings.AUTH_USER_MODEL,
        on_delete=models.CASCADE,
        verbose_name=_('领导'),
        related_name='user_relation_leader',
    )
    subordinate = models.ForeignKey(
        settings.AUTH_USER_MODEL,
        on_delete=models.CASCADE,
        verbose_name=_('下属'),
        related_name='user_relation_subordinate',
    )
    min_amount = models.DecimalField(_('最低审批金额'), null=True, default=0, decimal_places=2, max_digits=12)
    created_by = models.ForeignKey(
        settings.AUTH_USER_MODEL,
        on_delete=models.SET_NULL,
        null=True,
        blank=True,
        verbose_name=_('创建人'),
        related_name='user_relation_created_by',
    )
    updated_by = models.ForeignKey(
        settings.AUTH_USER_MODEL,
        on_delete=models.SET_NULL,
        null=True,
        blank=True,
        verbose_name=_('更新人'),
        related_name='user_relation_updated_by',
    )
    datetime_created = models.DateTimeField(_('创建时间'), auto_now_add=True)
    datetime_updated = models.DateTimeField(_('更新时间'), auto_now=True)

    objects = models.Manager()

    class Meta:
        ordering = ['-datetime_created']
        verbose_name = _('直属关系')
        verbose_name_plural = _('直属关系')

    def __str__(self):
        return "{0} {1}".format(
            self.leader,
            self.subordinate,
        )


class Project(models.Model):
    code = models.CharField(_('项目编号'), max_length=255, unique=True, null=True, blank=True)
    name = models.CharField(_('项目名称'), max_length=255, null=True, unique=True)
    desc = models.TextField(_('项目描述'), max_length=1000, null=True, blank=True)
    status = models.PositiveSmallIntegerField(_('项目状态'), choices=project_status_choices, default=1)
    related_person = models.ManyToManyField(
        settings.AUTH_USER_MODEL,
        blank=True,
        verbose_name=_('关联人员')
    )
    date_start = models.DateField(_('开始日期'),  null=True, blank=True)
    date_finish = models.DateField(_('截止日期'), null=True, blank=True)
    predict_amount = models.DecimalField(_('预算'), null=True, blank=True, decimal_places=2, max_digits=10)
    created_by = models.ForeignKey(
        settings.AUTH_USER_MODEL,
        on_delete=models.SET_NULL,
        null=True,
        blank=True,
        verbose_name=_('创建人'),
        related_name='project_created_by',
    )
    updated_by = models.ForeignKey(
        settings.AUTH_USER_MODEL,
        on_delete=models.SET_NULL,
        null=True,
        blank=True,
        verbose_name=_('更新人'),
        related_name='project_updated_by',
    )
    datetime_created = models.DateTimeField(_('创建时间'), auto_now_add=True)
    datetime_updated = models.DateTimeField(_('更新时间'), auto_now=True)

    objects = models.Manager()

    class Meta:
        ordering = ['-pk']
        verbose_name = _('项目')
        verbose_name_plural = _('项目')

    def get_absolute_url(self):
        return reverse('approval:projects')

    def __str__(self):
        return "{0}".format(
            self.name,
        )


class ApplicationType(models.Model):
    code = models.CharField(_('类型编号'), max_length=255, unique=True)
    name = models.CharField(_('类型名称'), max_length=255)
    category = models.SmallIntegerField(_('费用类型'), null=True, choices=application_type_categories_choices)
    is_show = models.BooleanField(_('显示'), default=True)
    is_count = models.BooleanField(_('是否统计'), default=True)

    objects = models.Manager()

    class Meta:
        ordering = ['code']
        verbose_name = _('申请类型')
        verbose_name_plural = _('申请类型')

    def __str__(self):
        return "{} {}".format(
            self.code,
            self.name,
        )


class Application(models.Model):
    code = models.CharField(_('编号'), max_length=255, null=True, blank=True, unique=True)
    name = models.CharField(_('报销内容'), max_length=255, null=True)
    related_money_flow = models.ForeignKey(
        'approval.MoneyFlow',
        on_delete=models.SET_NULL,
        null=True,
        blank=True,
        verbose_name=_('关联现金流')
    )
    related_project = models.ForeignKey(
        Project,
        null=True,
        on_delete=models.PROTECT,
        verbose_name=_('关联项目')
    )
    related_application_type = models.ForeignKey(
        ApplicationType,
        null=True,
        blank=True,
        on_delete=models.SET_NULL,
        verbose_name=_('费用类型')
    )
    related_department = models.ForeignKey(
        'oa.Department',
        null=True,
        on_delete=models.PROTECT,
        verbose_name=_('业务部门')
    )
    status = models.PositiveSmallIntegerField(_('申请状态'), choices=application_status_choices, default=1)
    initial_amount = models.DecimalField(_('报销金额'), max_digits=10, decimal_places=2, default=0)
    initial_notes = models.TextField(_('报销备注'), null=True, blank=True, max_length=1000)
    date_of_occurrence = models.DateField(_('起始日期'), null=True)
    dates = models.SmallIntegerField(_('天数'), default=1)
    date_end = models.DateField(_('截止日期'), null=True, blank=True)
    approval_amount = models.DecimalField(_('审批金额'), max_digits=10, decimal_places=2, null=True, blank=True)
    approval_notes = models.TextField(_('审批备注'), null=True, blank=True, max_length=1000)
    has_invoice = models.BooleanField(_('有无发票'), default=True)
    pay_channel = models.PositiveSmallIntegerField(_('打款选项'), null=True, blank=True, choices=pay_channel_choices)
    default_approved_by = models.ForeignKey(
        settings.AUTH_USER_MODEL,
        on_delete=models.SET_NULL,
        null=True,
        blank=True,
        verbose_name=_('默认审批'),
        related_name='application_approved_by',
    )
    created_by = models.ForeignKey(
        settings.AUTH_USER_MODEL,
        on_delete=models.SET_NULL,
        null=True,
        blank=True,
        verbose_name=_('创建人'),
        related_name='application_created_by',
    )
    updated_by = models.ForeignKey(
        settings.AUTH_USER_MODEL,
        on_delete=models.SET_NULL,
        null=True,
        blank=True,
        verbose_name=_('更新人'),
        related_name='application_updated_by',
    )
    datetime_created = models.DateTimeField(_('创建时间'), auto_now_add=True)
    datetime_updated = models.DateTimeField(_('更新时间'), auto_now=True)

    objects = models.Manager()

    @property
    def status_number(self):
        application_status_percentage = {
            1: 20, 2: 40, 3: 60, 4: 80, 5: 60, 6: 100, 7: 100
        }
        return application_status_percentage.get(int(self.status), 0)

    @property
    def date_info(self):
        if self.date_end and self.dates > 1:
            return '{} 至 {}'.format(self.date_of_occurrence, self.date_end)
        else:
            return '{}'.format(self.date_of_occurrence)

    class Meta:
        ordering = ['-pk']
        verbose_name = _('申请')
        verbose_name_plural = _('申请')

    def __str__(self):
        return "{0} {1}".format(
            self.code,
            self.name,
        )

    def attachments_property(self):
        return ApplicationAttachment.objects.filter(related_application_id=self.pk)

    attachments_property.short_description = "批次详情"
    attachments = property(attachments_property)

    def get_absolute_url(self):
        return reverse('approval:application', args=[self.pk])


class ApplicationAttachment(models.Model):
    file_name = models.CharField(_('附件名称'), max_length=255, null=True, blank=True)
    file = models.FileField(_('文件'), null=True, upload_to='application/%Y/%m/%d/')
    related_application = models.ForeignKey(
        Application,
        on_delete=models.CASCADE,
        verbose_name=_('关联申请')
    )
    datetime_created = models.DateTimeField(_('创建时间'), auto_now_add=True)
    datetime_updated = models.DateTimeField(_('更新时间'), auto_now=True)

    objects = models.Manager()

    class Meta:
        ordering = ['pk']
        verbose_name = _('申请附件')
        verbose_name_plural = _('申请附件')

    def __str__(self):
        return "{0}".format(
            self.file_name,
            self.file
        )


class ApprovalRecord(models.Model):
    related_application = models.ForeignKey(
        Application,
        on_delete=models.CASCADE,
        verbose_name=_('关联申请')
    )
    pre_status = models.PositiveSmallIntegerField(
        _('初始状态'), choices=application_status_choices, null=True, blank=True)
    post_status = models.PositiveSmallIntegerField(
        _('之后状态'), choices=application_status_choices, null=True, blank=True)
    related_user = models.ForeignKey(
        settings.AUTH_USER_MODEL,
        on_delete=models.PROTECT,
        verbose_name=_('操作人员'),
    )
    datetime_created = models.DateTimeField(_('创建时间'), auto_now_add=True)
    datetime_updated = models.DateTimeField(_('更新时间'), auto_now=True)

    objects = models.Manager()

    class Meta:
        ordering = ['-pk']
        verbose_name = _('审批过程记录')
        verbose_name_plural = _('审批过程记录')

    def __str__(self):
        return "{0} {1}-->{2}".format(
            self.related_application,
            self.pre_status,
            self.post_status
        )


class OppositeAccount(models.Model):
    name = models.CharField(_('科目名称'), max_length=255, null=True, unique=True)

    objects = models.Manager()

    class Meta:
        ordering = ['pk']
        verbose_name = _('科目列表')
        verbose_name_plural = _('科目列表')

    def __str__(self):
        return "{0}".format(self.name)


class MoneyFlow(models.Model):
    application_type = models.CharField(_('费用类型'), null=True, blank=True, max_length=200)
    related_application_type = models.ForeignKey(
        ApplicationType,
        null=True,
        on_delete=models.PROTECT,
        verbose_name=_('关联费用类型')
    )
    record_date = models.DateField(_('日期'), null=True, blank=True, db_index=True)
    record_code = models.CharField(_('凭证字号'), null=True, blank=True, max_length=200)
    record_notes = models.CharField(_('摘要'), null=True, blank=True, max_length=200)
    opposite_account = models.CharField(_('对方科目'), null=True, blank=True, max_length=200)
    related_opposite_account = models.ForeignKey(
        OppositeAccount,
        null=True,
        on_delete=models.PROTECT,
        verbose_name=_('关联对方科目')
    )
    debit_side = models.DecimalField(_('借方'), null=True, blank=True, decimal_places=2, max_digits=12)
    credit_side = models.DecimalField(_('贷方'), null=True, blank=True, decimal_places=2, max_digits=12)
    direction = models.SmallIntegerField(_('方向'), null=True, blank=True, choices=direction_choices)

    objects = models.Manager()

    class Meta:
        ordering = ['-record_date']
        verbose_name = _('支付明细')
        verbose_name_plural = _('支付明细')

    def __str__(self):
        return "{} {} {} {} {} {}".format(
            self.record_code,
            self.record_date,
            self.application_type,
            self.debit_side,
            self.direction,
            self.record_notes
        )

    @mark_safe
    def cost_split_list(self):
        res = ''
        css = CostSplit.objects.filter(
            related_money_flow_id=self.pk
        ).values(
            'related_department__name',
            'related_application_type__name',
            'amount'
        )
        res += '<ul>'
        for cs in css:
            res += '<li style="white-space: nowrap;">{0} ({1}) {2} 元</li>'.format(
                cs['related_department__name'],
                cs['related_application_type__name'],
                cs['amount'],
            )
        res += '</ul>'
        return res

    cost_split_list.short_description = "拆分明细"
    cost_split_ls = property(cost_split_list)


class CostSplit(models.Model):
    related_money_flow = models.ForeignKey(
        MoneyFlow,
        on_delete=models.PROTECT,
        null=True,
        blank=True,
        related_name='cost_split',
        verbose_name=_('关联现金流')
    )
    related_application = models.ForeignKey(
        Application,
        on_delete=models.PROTECT,
        null=True,
        blank=True,
        verbose_name=_('关联申请')
    )
    related_department = models.ForeignKey(
        'oa.Department',
        null=True,
        blank=True,
        on_delete=models.PROTECT,
        verbose_name=_('部门')
    )
    record_date = models.DateField(_('日期'), null=True, blank=True, db_index=True)
    amount = models.DecimalField(_('金额'), null=True, blank=True, decimal_places=2, max_digits=12)
    related_application_type = models.ForeignKey(
        ApplicationType,
        null=True,
        blank=True,
        on_delete=models.PROTECT,
        verbose_name=_('关联费用类型')
    )

    objects = models.Manager()

    class Meta:
        ordering = ['-record_date']
        verbose_name = _('成本拆分')
        verbose_name_plural = _('成本拆分')

    def __str__(self):
        return "{} {} {} {} 元".format(
            self.related_department,
            self.related_application_type,
            self.record_date,
            self.amount,
        )


def get_related_users(request, s='all'):
    user_id = request.user.pk
    related_users_id_list = []
    if s == 'subordinates':
        related_users = UserRelation.objects.filter(
            leader_id=user_id
        ).values('subordinate__pk').distinct()
    elif s == 'subordinates_and_own':
        related_users = UserRelation.objects.filter(
            leader_id=user_id
        ).values('subordinate__pk').distinct()
        related_users_id_list.append(user_id)
    else:
        related_users = UserRelation.objects.filter(
            Q(leader_id=user_id) | Q(subordinate_id=user_id)
        ).values('leader__pk', 'subordinate__pk').distinct()
        related_users_id_list.append(user_id)
    for u in related_users:
        if u.get('leader__pk'):
            if u['leader__pk'] not in related_users_id_list:
                related_users_id_list.append(u['leader__pk'])
        if u.get('subordinate__pk'):
            if u['subordinate__pk'] not in related_users_id_list:
                related_users_id_list.append(u['subordinate__pk'])
    return related_users_id_list


@receiver(pre_save, sender=Application)
def pre_save_application(sender, instance, **kwargs):
    # 计算 code
    pre_record = Application.objects.order_by('pk').values('pk').last()
    if pre_record:
        pre_count = pre_record['pk']
    else:
        pre_count = 0
    if not instance.code:
        instance.code = '{0}-{1}-{2}'.format(
            date.today().strftime('%y%m%d'),
            '{}'.format(pre_count+1).zfill(6),
            '{}'.format(random.randrange(1, 999)).zfill(3)
        )
    if instance.approval_amount is None:
        instance.approval_amount = instance.initial_amount
    if instance.date_of_occurrence and instance.dates:
        instance.date_end = instance.date_of_occurrence + timedelta(days=(int(instance.dates)-1))
    # 默认审核人员
    if not instance.default_approved_by:
        if instance.created_by:
            default_approved_by = UserRelation.objects.filter(
                subordinate=instance.created_by,
                min_amount__lte=instance.initial_amount
            ).order_by('min_amount').last()
            if default_approved_by:
                instance.default_approved_by = default_approved_by.leader


@receiver(pre_save, sender=ApplicationAttachment)
def pre_save_application_attachment(sender, instance, **kwargs):
    if instance.file and not instance.file_name:
        instance.file_name = instance.file.name


@receiver(pre_save, sender=CostSplit)
def pre_save_cost_split(sender, instance, **kwargs):
    if instance.related_money_flow:
        instance.record_date = instance.related_money_flow.record_date
        instance.related_application_type = instance.related_money_flow.related_application_type
        if not instance.amount:
            instance.amount = instance.related_money_flow.amount
